<!doctype html>
<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
  <meta charset="utf-8">
  <script src="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
  <script src="wct-browser-config.js"></script>
  <script src="../../node_modules/wct-browser-legacy/browser.js"></script>
  <script type="module" src="../../polymer-legacy.js"></script>
</head>
<body>

<script type="module">
window.clearTestLists = function() {
  window.actualAttachedList = [];
  window.actualReadyList = [];
  window.actualReadyBeforeAttachedList = [];
};

window.clearTestLists();

window.readyMixin = function(base) {
  return class readyMixin extends base {
    static get properties() {
      return {
        prop: {
          value: true,
          observer: '_propChanged'
        }
      };
    }

    _propChanged() {
      this.observerShadowRoot = Boolean(this.shadowRoot);
    }
    ready() {
      super.ready();
      this._readied = true;
      this.readyList = window.actualReadyList.slice();
      this.readyShadowRoot = Boolean(this.shadowRoot);
      window.actualReadyList.push(this);
    }

    connectedCallback() {
      this._eventList = [];
      this.addEventListener('e', (e) => {
        this._eventList.push(e.composedPath()[0]);
      });
      super.connectedCallback();
      this.dispatchEvent(new Event('e', {composed: true, bubbles: true}));
      this.attachedShadowRoot = Boolean(this.shadowRoot);
      this.attachedTime$Keys = Object.keys(this.$);
      this.attachedList = window.actualAttachedList.slice();
      window.actualAttachedList.push(this);
      if (!this._readied) {
        window.actualReadyBeforeAttachedList.push(this);
      }
    }
  };
};
</script>

<dom-module id="x-zot">
  <template>
    x-zot<slot></slot>
  </template>
  <script type="module">
import { PolymerElement } from '../../polymer-element.js';
class El extends window.readyMixin(PolymerElement) {
  static get is() { return 'x-zot'; }
}
customElements.define(El.is, El);
</script>
</dom-module>

<dom-module id="x-bar">
  <template>
    <x-zot id="zot"></x-zot>
  </template>
  <script type="module">
import { PolymerElement } from '../../polymer-element.js';
class El extends window.readyMixin(PolymerElement) {
  static get is() { return 'x-bar'; }
}
customElements.define(El.is, El);
</script>
</dom-module>

<dom-module id="x-foo">
  <template>
    <x-bar id="bar1"></x-bar>
    <x-bar id="bar2"></x-bar>
  </template>
  <script type="module">
import { PolymerElement } from '../../polymer-element.js';
class El extends window.readyMixin(PolymerElement) {
  static get is() { return 'x-foo'; }
}
customElements.define(El.is, El);
</script>
</dom-module>

<dom-module id="x-ready">
  <template>
    <x-zot id="a">
      <x-zot id="b"></x-zot>
      <x-zot id="c">
        <x-zot id="d"></x-zot>
      </x-zot>
    </x-zot>
    <x-foo id="foo"></x-foo>
  </template>
  <script type="module">
import { PolymerElement } from '../../polymer-element.js';
class El extends window.readyMixin(PolymerElement) {
  static get is() { return 'x-ready'; }
  static get properties() {
    return {
      foo: {
        observer: '_fooChanged'
      }
    };
  }
  _fooChanged() {}
}
customElements.define(El.is, El);
</script>
</dom-module>

<dom-module id="x-templatized">
  <template>
    <template is="dom-if" if>
      <x-ready foo="[[foo]]"></x-ready>
    </template>
  </template>
  <script type="module">
import { PolymerElement } from '../../polymer-element.js';
class El extends PolymerElement {
  static get is() { return 'x-templatized'; }
  static get properties() {
    return {
      foo: {
        value: 'foo'
      }
    };
  }
}
customElements.define(El.is, El);
</script>
</dom-module>

<script type="module">
import { flush } from '../../lib/utils/flush.js';

suite('ready and attached ordering', function() {

  let el;

  setup(function() {
    window.clearTestLists();
    el = document.createElement('x-ready');
    document.body.appendChild(el);
  });

  teardown(function() {
    document.body.removeChild(el);
  });

  test('element dom ready before element', function() {
    assert.includeMembers(el.readyList, [el.$.a, el.$.b, el.$.c, el.$.d, el.$.foo]);
    var foo = el.$.foo;
    assert.includeMembers(foo.readyList, [foo.$.bar1, foo.$.bar1.$.zot, foo.$.bar2, foo.$.bar2.$.zot]);
    var b1 = foo.$.bar1, b2 = foo.$.bar2;
    assert.includeMembers(b1.readyList, [b1.$.zot]);
    assert.includeMembers(b2.readyList, [b2.$.zot]);
  });

  test('can listen to events fired by element dom in connected', function() {
    assert.includeMembers(el._eventList, [el.$.a, el.$.b, el.$.c, el.$.d, el.$.foo]);
    var foo = el.$.foo;
    assert.includeMembers(foo._eventList, [foo.$.bar1, foo.$.bar2]);
    var b1 = foo.$.bar1, b2 = foo.$.bar2;
    assert.includeMembers(b1._eventList, [b1.$.zot]);
    assert.includeMembers(b2._eventList, [b2.$.zot]);
  });

  test('shadowRoot available in ready, connected, observer', function() {
    [el, el.$.a, el.$.b, el.$.c, el.$.d, el.$.foo,
      el.$.foo.$.bar1, el.$.foo.$.bar1.$.zot,
      el.$.foo.$.bar2, el.$.foo.$.bar2.$.zot].forEach((e) => {
      assert.isTrue(e.observerShadowRoot);
      assert.isTrue(e.readyShadowRoot);
      assert.isTrue(e.attachedShadowRoot);
    });
  });

  test('element attached called after ready', function() {
    assert.equal(window.actualReadyBeforeAttachedList.length, 0);
  });

  test('element has $ references at attached time', function() {
    assert.sameMembers(el.attachedTime$Keys, ['a', 'b', 'c', 'd', 'foo']);
    assert.sameMembers(el.$.foo.attachedTime$Keys, ['bar1', 'bar2']);
  });

});

suite('templatized: ready and attached ordering', function() {

  let container, el;

  setup(function() {
    window.clearTestLists();
    container = document.createElement('x-templatized');
    document.body.appendChild(container);
    flush();
    el = container.shadowRoot.querySelector('x-ready');
  });

  teardown(function() {
    document.body.removeChild(container);
  });

  test('element dom ready before element', function() {
    assert.includeMembers(el.readyList, [el.$.a, el.$.b, el.$.c, el.$.d, el.$.foo]);
    var foo = el.$.foo;
    assert.includeMembers(foo.readyList, [foo.$.bar1, foo.$.bar1.$.zot, foo.$.bar2, foo.$.bar2.$.zot]);
    var b1 = foo.$.bar1, b2 = foo.$.bar2;
    assert.includeMembers(b1.readyList, [b1.$.zot]);
    assert.includeMembers(b2.readyList, [b2.$.zot]);
  });

  test('can listen to events fired by element dom in connected', function() {
    assert.includeMembers(el._eventList, [el.$.a, el.$.b, el.$.c, el.$.d, el.$.foo]);
    var foo = el.$.foo;
    assert.includeMembers(foo._eventList, [foo.$.bar1, foo.$.bar2]);
    var b1 = foo.$.bar1, b2 = foo.$.bar2;
    assert.includeMembers(b1._eventList, [b1.$.zot]);
    assert.includeMembers(b2._eventList, [b2.$.zot]);
  });

  test('shadowRoot available in ready, connected, observer', function() {
    [el, el.$.a, el.$.b, el.$.c, el.$.d, el.$.foo,
      el.$.foo.$.bar1, el.$.foo.$.bar1.$.zot,
      el.$.foo.$.bar2, el.$.foo.$.bar2.$.zot].forEach((e) => {
      assert.isTrue(e.observerShadowRoot);
      assert.isTrue(e.readyShadowRoot);
      assert.isTrue(e.attachedShadowRoot);
    });
  });

  test('element attached called after ready', function() {
    assert.equal(window.actualReadyBeforeAttachedList.length, 0);
  });

  test('element has $ references at attached time', function() {
    assert.sameMembers(el.attachedTime$Keys, ['a', 'b', 'c', 'd', 'foo']);
    assert.sameMembers(el.$.foo.attachedTime$Keys, ['bar1', 'bar2']);
  });

});
</script>
</body>
</html>
